use super::pcap_file::{PcapFile, parse_i32, parse_u16, parse_u32};
use crate::common::error_enum::ErrorEnum;
use chrono::{Local, TimeZone};
use core::fmt;
use std::fs::File;
use std::io::SeekFrom::Start;
use std::io::{BufReader, Read, Seek};

pub const PCAP_HDR_SIZE: usize = 24;
pub const PCAP_PRERECORD_HDR_SIZE: usize = 16;

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Endianness {
    BigEndian,
    LittleEndian,
}

#[derive(Debug, PartialEq, Clone, Copy)]
pub enum FileFormat {
    Standard, // 标准 lib pcap 格式
    Nsg,      // 非标准格式
}

pub struct PcapHdr {
    magic_number: u32,  // magic number
    version_major: u16, // major version number
    version_minor: u16, // minor version number
    this_zone: i32,     // GMT to local correction
    sig_figs: u32,      // accuracy of timestamps
    snap_len: u32,      // max length of captured packets
    network: u32,       // date link type
}

impl PcapHdr {
    pub fn new(pcap_file_buf: &mut BufReader<File>) -> Result<Self, ErrorEnum> {
        let (endianness, _) = PcapFile::get_pcap_format(pcap_file_buf)?;
        pcap_file_buf.seek(Start(0))?;
        let mut buf = vec![0; PCAP_HDR_SIZE];
        let bytes_read_num = pcap_file_buf.read(&mut buf)?;
        if bytes_read_num != PCAP_HDR_SIZE {
            return Err(ErrorEnum::FileError);
        }
        let magic_number = parse_u32([buf[0], buf[1], buf[2], buf[3]], endianness);

        let version_major = parse_u16([buf[4], buf[5]], endianness) as u16;

        let version_minor = parse_u16([buf[6], buf[7]], endianness) as u16;
        Self::validate_version(version_major, version_minor)?;
        let this_zone = parse_i32([buf[8], buf[9], buf[10], buf[11]], endianness);

        let sig_figs = parse_u32([buf[12], buf[13], buf[14], buf[15]], endianness) as u32;

        let snap_len = parse_u32([buf[16], buf[17], buf[18], buf[19]], endianness) as u32;

        let network = parse_u32([buf[20], buf[21], buf[22], buf[23]], endianness) as u32;

        Ok(Self {
            magic_number,
            version_major,
            version_minor,
            this_zone,
            sig_figs,
            snap_len,
            network,
        })
    }
    fn validate_version(major: u16, minor: u16) -> Result<(), ErrorEnum> {
        // 只支持 2.4 版本
        if major != 2 || minor != 4 {
            Err(ErrorEnum::UnsupportedVersion(major, minor))
        } else {
            Ok(())
        }
    }
}

#[derive(Debug)]
pub struct PcapRecordHdr {
    pub ts_sec: u32,   //timestamp seconds
    pub ts_usec: u32,  //timestamp microseconds
    pub incl_len: u32, //number os octets of packet saved ind file
    pub orig_len: u32, //actual length of packet
}

impl fmt::Display for PcapRecordHdr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // 使用 timestamp_opt() 处理可能的无效时间戳
        let datetime = match Local.timestamp_opt(self.ts_sec as i64, (self.ts_usec * 1000) as u32) {
            chrono::LocalResult::Single(dt) => dt,
            chrono::LocalResult::Ambiguous(dt, _) => dt, // 取第一个可能的时间点
            chrono::LocalResult::None => {
                // 处理无效时间戳的情况，使用当前时间或其他默认值
                Local::now()
            }
        };

        // 微秒部分
        let micros = self.ts_usec % 1_000_000;

        // 格式化输出
        write!(
            f,
            "Frame time: {} {}.{:06} {}\n\
             Frame included length: {} bytes\n\
             Frame original length: {} bytes",
            datetime.format("%b %d, %Y %H:%M:%S"),
            datetime.timestamp(),
            micros,
            datetime.format("%Z"),
            self.incl_len,
            self.orig_len
        )
    }
}

impl PcapRecordHdr {
    pub fn new(
        buf: &mut Vec<u8>,
        (endianness, _): (Endianness, FileFormat),
    ) -> Result<Self, ErrorEnum> {
        let ts_sec = parse_u32([buf[0], buf[1], buf[2], buf[3]], endianness);

        let ts_usec = parse_u32([buf[4], buf[5], buf[6], buf[7]], endianness);

        let incl_len = parse_u32([buf[8], buf[9], buf[10], buf[11]], endianness);

        let orig_len = parse_u32([buf[12], buf[13], buf[14], buf[15]], endianness);

        Ok(Self {
            ts_sec,
            ts_usec,
            incl_len,
            orig_len,
        })
    }
}
